// class constructors do not return anything


inline
kmeans_regions::kmeans_regions(const std::string field_means, const int in_Ncent)
:N_cent(in_Ncent)
{
  Mean_segment.load(field_means);
  N_data = Mean_segment.n_rows;
  N_reg = Mean_segment(0).n_cols;
  Dim = Mean_segment(0).n_rows;
  
  cout << "N_data: " << N_data << endl;
  cout << "N_reg: " << N_reg << endl;
  cout << "Dim: " << Dim << endl;
  cout << "Cent: " << N_cent<< endl;
  //getchar();
}


inline
void
kmeans_regions::run(const uword max_iter)
{
  //cout << "Dim = " << Dim << endl;
  uword iter = 0;
  umat comparing;
  //ini_cent();// Initializing cent_1
  init_partitions();
  
  do
  {
    cout << iter << ". ";
    update_cent(); //means are updated inside this method
    comparing = (partitions != new_partitions);
    partitions = new_partitions;
    //getchar();
    
    
    if (iter < max_iter)
    {
        err = sum(sum(comparing))/(N_cent);      
    }
    else
    {
      err = 0;
      distortion = 0;  // -- Nov15,2013 
    }
    cout << "- Err= " << err << " & distortion= " << distortion << endl; // -- Nov15,2013 
    iter++;
  }while ( (distortion !=0 )  );
  cout << endl;
  partitions.save("part_Dec17.dat", raw_ascii);
  counts.save("counts_Dec17.dat", raw_ascii);
  
}


inline
void
kmeans_regions::init_partitions()
{
  vec tmp = randu<vec>(N_data ) * (N_cent);
  //tmp.print("tmp:");
  uvec tmp2 = conv_to<uvec>::from( tmp );
  //tmp2.t().print("tmp2:");
  //getchar();
  
  partitions.zeros(N_data,N_cent);
  //new_partitions.zeros(N_data,N_cent);
  
  counts.zeros(N_cent);
  cent_segms.set_size(N_cent);
  
  for (uword c = 0 ; c < N_cent; ++c)
  {
    uvec q1 = find (tmp2 == c);
    counts(c) = q1.n_elem;
    //q1.t().print("q1 bf: ");
    q1.resize(N_data);
    //q1.t().print("q1 after: ");
    //getchar();
    partitions.col( c ) = q1;
    mat cent_i = zeros<mat>(Dim,N_reg);
    
    for (uword i=0; i < counts(c); ++i)
    {
      cent_i+=Mean_segment(q1(i));
    }
    cent_i/=counts(c);
    cent_segms(c) = cent_i;
  }
  //partitions.print("initial_indices:");
  //counts.t().print("initial counts:");
  cout << endl;
  //getchar();
  
}




/*
 * inline
 * void 
 * kmeans_regions::ini_cent()
 * {
 *  //cout << "...Doing init_cent";
 *  
 *  vec tmp = randu<vec>(N_cent) * (N_data); 
 *  initial_indices = conv_to<uvec>::from( tmp );// converting to <uvec> from <vec>
 *  //cent_1.zeros(Dim, N_cent);
 *  initial_indices.t().print("initial_indices");
 *  //   for(uword ci=0; ci < N_cent; ++ci)
 *  //   {
 *    //     //cent_1.col(ci) = data.col( initial_indices(ci));
 *  //     //Descomentar
 *  //     //cout << "Initial Centroid corresponds to: " << initial_indices(ci) << " is " << labels_actions(initial_indices(ci),0) << endl;
 *  //   }
 *  
 *  
 }*/

inline
void 
kmeans_regions::update_cent()
{
  //uword c_idx;
  //cout << "...doing update cent" ;
  new_partitions.zeros(N_data,N_cent);
  
  //indices2.zeros(N_cent);
  counts.zeros(N_cent);
  
  
  distortion = 0;// Nov15,2013
  Points_cent.zeros(N_data, N_cent); // Nov15,2013
  
  for(uword ni = 0; ni < N_data; ++ni)
  {
    distances.zeros(N_cent);
    for(uword ci = 0; ci < N_cent; ++ci)
    {
      
      for (uword ri = 0 ; ri < N_reg; ++ri)
      {
	
	vec mean_nri = Mean_segment(ni).col(ri)  ; // mean Segment ni in region i
	//mean_nri.print("mean_nri");
	vec mean_cri = cent_segms(ci).col(ri); // mean Centroid ci in region i
	//mean_cri.print("mean_cri");
	
	
	distances(ci) += norm( mean_nri - mean_cri , 2 );
	//distances(ci) += norm( mean_nri - mean_cri , 1 );//Probar con este tambien
	//distances(ci) = norm( (data.col(ni) - cent_1.col(ci)), 2 );
	//distances.t().print("for region");
	//getchar();
      }
      //distances.t().print("dist per region");
      //getchar();
      
    }
    //distances.t().print("distances: ");
    //getchar();
    double mind = floor(distances.min(best_ci)/(N_cent*N_reg));
    distortion+=mind;
    
    
    new_partitions(counts(best_ci),best_ci)  = ni;
    Points_cent(counts(best_ci),best_ci) = mind;
    counts(best_ci) += 1;  
    //counts2.t().print("counts tmp: ");
    //getchar();
  }
  
  //counts.t().print("counts: ");
  //Points_cent.save("points_cent.dat", raw_ascii);
  //getchar();

  
 for (uword c = 0 ; c < N_cent; ++c)
  {
    uvec q1 = new_partitions.col(c);
    //counts(c) = q1.n_elem;
    //q1.t().print("q1 bf: ");
    q1.resize(counts(c));
    //q1.t().print("q1 after: ");
    //getchar();
    mat cent_i = zeros<mat>(Dim,N_reg);
    
    for (uword i=0; i < counts(c); ++i)
    {
      cent_i+=Mean_segment(q1(i));
    }
    cent_i/=counts(c);
    cent_segms(c) = cent_i;
  }
  
  
}


///AQUI VOY!!!

///Ahora a calcular los clusters mas chiquis. 
/// Y a graficar y ver que dar..... Chulitos ;)


inline
uvec
kmeans_regions::small_clusters(uword n_segm)
{
  
  uvec summary;
  summary.zeros(N_data); // Summary with points in smaller clusters.
  
  uvec indices = sort_index(counts);
  //counts.t().print("Count in sort");;
  //getchar();
  //indices.t().print("Sorted: ");
  //cout << "Summary 10% " << endl;
  //int leng_summ = floor(N_cent/10);
  int pos = 0;
  int i = 0;
  
  while (pos < n_segm) // N_cent = 10% of Segments
    {
      uword le = counts(indices(i)); // Cuantos elementos hay
      //cout << "le = " << le << endl;
      for (uword j = 0; j < le; ++j)
      {
	summary(pos) = partitions (j, indices(i) ); 
	//cout << "le = " << le << endl;
	//cout << "elem to add " << partitions (j, indices(i) ) << endl; 
	//cout << " " << pos ;
	pos++;
	//getchar();
      }
      i++;
      //summ.t().print("summ");
    }
    //cout << "aqui??" << endl;
    summary.resize(pos);
    //cout << "o aqui??" << endl;
    //summ.t().print("summ");
    return sort( summary);
}


inline
uvec
kmeans_regions::near_points() // Check it! I implement but I haven't checked
{
  //To select the closest point to the centrioid.
  uvec point_cluster; // Indeces of the cluster point to each centroid
  point_cluster.zeros(counts.n_elem);   //Closest point per cluster
  
  uvec elem_cluster; // Number of elem per
  point_cluster.zeros(counts.n_elem);
  
  
  
  vec dist_ci;
  uword  index;
  //double min_val;
  int j =0;
  for (uword i = 0; i < counts.n_elem ; ++i)
  {
    if (counts(i)>0)
    {
      dist_ci.zeros(counts(i));
      dist_ci = Points_cent( span(0, counts(i) - 1) , i );
      dist_ci.min(index);
      point_cluster (j) = partitions(index,i) + 1; 
      j++;
    }
    
  }
  
  
  point_cluster.resize(j);
  //point_cluster.t().print("point_cluster");
  //getchar();
  
  return sort(point_cluster);
}

